#include <linux/string.h>
#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/udp.h>
#include <linux/types.h> 
#include <linux/proc_fs.h>
#include <linux/fcntl.h> 
#include <linux/device.h> 
#include <linux/cdev.h>
#include <linux/sysfs.h>
#include <linux/mm.h>
#include <linux/debugfs.h>
#include <linux/init.h>

#include "fw4.h"
#include "fw4_add.h"
#include "hooks.c"
#include "hooks.h"
#include "devices.h"
#include "devices.c"
#include "memory.c"
#include "memory.h"

MODULE_AUTHOR("Ahia Victor");
MODULE_DESCRIPTION("firewall deivce");
MODULE_LICENSE("GPL");

module_init(r_init);
module_exit(r_cleanup);
static void freeAll(void);

// main function
static int r_init(void){		
	int result = -1;
	// create class 
	if ((cl = class_create(THIS_MODULE, CLASS_NAME)) == NULL){		
		printk("class create failed \n");
		return -1;
	}
	
	// allocate region for fw4_rules deivce
	if((result = alloc_chrdev_region(&rulesDev,0,RULES_FILES,RULES_NAME))<0){
		printk("firewall module failed to register\n");
		class_destroy(cl);
		return result;
	}
	
	 // create device fw4_rules
	 if ((dv = device_create(cl, NULL,rulesDev, MKDEV(major, 1), RULES_NAME)) == NULL ){		
		unregister_chrdev_region(rulesDev, RULES_FILES);
		class_destroy(cl);
		printk("device create failed (rules)\n");
		return -1;
    }
    cdev_init(&rulesCdev, &rules_fops);
    
    if (cdev_add(&rulesCdev, rulesDev, 1) == -1){
		device_destroy(cl, rulesDev);		
		class_destroy(cl);
		unregister_chrdev_region(rulesDev, RULES_FILES);		
		printk("cdev_add failed (rules)\n");
		return -1;
    }
	
	// create files for fw4_rules
	if(device_create_file(dv,&config_attrs)<0 || device_create_file(dv,&rules_size_attrs)<0){
		freeAll();
		printk("device_create_file failed \n");
		return -1;
	}


	// allocate region for fw4_log deivce
	if((result = alloc_chrdev_region(&logDev,0,LOG_FILES,LOG_NAME))<0){
		printk("firewall module failed to register\n");
		freeAll();
		return result;
	}
	
	 // create device fw4_log
	 if ((dv = device_create(cl, NULL,logDev, MKDEV(major, 1), LOG_NAME)) == NULL ){		
		freeAll();
		return -1;
    }
    cdev_init(&logCdev, &log_fops);
    
    if (cdev_add(&logCdev, logDev, 1) == -1){
		freeAll();
		printk("cdev_add failed (rules)\n");
		return -1;
    }
	
	// create files for fw4_log
	if(device_create_file(dv,&log_size_attrs)<0 || device_create_file(dv,&log_clear_attrs)<0){
		freeAll();
		printk("device_create_file failed \n");
		return -1;
	}

		// allocate region for fw4_conn_tab deivce
	if((result = alloc_chrdev_region(&connDev,0,CONN_FILES,CONN_NAME))<0){
		printk("firewall module failed to register\n");
		freeAll();
		return result;
	}
	
	 // create device fw4_conn_tab
	 if ((dv = device_create(cl, NULL,connDev, MKDEV(major, 1), CONN_NAME)) == NULL ){		
		freeAll();
		return -1;
    }
    cdev_init(&connCdev, &conn_fops);
    
    if (cdev_add(&connCdev, connDev, 1) == -1){
		freeAll();
		printk("cdev_add failed (rules)\n");
		return -1;
    }
	
	// create files for fw4_conn_tab
	if(device_create_file(dv,&conn_size_attrs)<0 || device_create_file(dv,&conn_clear_attrs)<0){
		freeAll();
		printk("device_create_file failed \n");
		return -1;
	}

    ////////////////////////////////
    // continue with other devices//
    ////////////////////////////////
    
    // register hooks
	if (nf_register_hook(&handler_ops)) {
		printk("couldnt register netfilter hook\n");
		r_cleanup();	
		return -1; 
	}	
	// allocate memory
	rulesPtr = (rule_t*)allocateMemory(sizeof(rule_t)*RULE_BASE_ENTRIES);
	// same for log
	initLog();
	// same for conn_table
	connTablePtr = (connection_t *)allocateMemory(sizeof(connection_t)*CONNECTION_TABLE_ENTRIES);
	
	// load default rules
	loadDefaultRules();
	
	printk("\nfirewall module registered\n");	
	return 0;
}

static void freeAll(void){
	if(dv != NULL){
		device_remove_file(dv,&config_attrs);
		device_remove_file(dv,&rules_size_attrs);
		device_remove_file(dv,&log_clear_attrs);
		device_remove_file(dv,&log_size_attrs);
		device_remove_file(dv,&conn_clear_attrs);
		device_remove_file(dv,&conn_size_attrs);
	}
	cdev_del(&rulesCdev);
	cdev_del(&logCdev);
	cdev_del(&connCdev);
	if(cl != NULL){	
		device_destroy(cl, rulesDev);	
		device_destroy(cl, logDev);	
		device_destroy(cl, connDev);
		class_destroy(cl);
	}
	unregister_chrdev_region(rulesDev, RULES_FILES);	
	unregister_chrdev_region(logDev, RULES_FILES);
	unregister_chrdev_region(connDev, RULES_FILES);
	nf_unregister_hook(&handler_ops);
	releaseAllMemory();		
}

// cleanup function
static void r_cleanup(void){	
	freeAll();
	printk(KERN_INFO "\nfirewall unregistered\n");	
}





